package demo.java.lang.reflect;

import com.alibaba.fastjson.JSONObject;
import demo.vo.People;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Type是Java 编程语言中所有类型的公共高级接口（官方解释），也就是Java中所有类型的父类；Type体系中类型的包括：
 * <li>ParameterizedType 参数化类型，即泛型；例如：List<T>、Map<K,V>等带有参数化的对象;
 * <li>TypeVariable 类型变量，即泛型中的变量；例如：T、K、V等变量，可以表示任何类；在这需要强调的是，TypeVariable代表着泛型中的变量，而ParameterizedType则代表整个泛型；
 * <li>GenericArrayType 泛型数组类型，用来描述ParameterizedType、TypeVariable类型的数组；即List<T>[] 、T[]等；
 * <li>Class是Type的一个实现类，属于原始类型，是Java反射的基础，对Java类的抽象；
 * 不仅仅包含我们平常所指的类，还包括枚举、数组、注解等；在程序运行期间，每一个类都对应一个Class对象，这个对象包含了类的修饰符、方法，属性、构造等信息，
 * <li>WildcardType泛型表达式（或者通配符表达式），即？ extend Number、？ super Integer这样的表达式；WildcardType虽然是Type的子接口，但却不是Java类型中的一种；
 *
 */
public class TypeDemo<T> {

    private static Logger logger = LoggerFactory.getLogger(TypeDemo.class);

    // ParameterizedType
    public Collection<String> collection = null;
    public Map<String, Integer> map = null;
    public Map.Entry<String, Integer> mapEntry = null;
    // GenericArrayType
    public List<String>[] listArray;
    public Set<String>[][] setArray;

    public T[] array;

    public static void main(String[] args) {
        Class<?> cls = People.class;
        Type[] types = cls.getGenericInterfaces();
        for (Type type : types) {
            logger.info(type.getTypeName() + " : " + type.toString());
            logger.info(type.getClass().toString());
        }

    }

    /**
     * var 局部变量推导
     * 对于某些可以直接推导出类型的局部变量，可以使用var进行声明。
     */
    @Test
    public void testVar() {
        var nums = new int[]{1, 2, 3, 4, 5};
        var sum = Arrays.stream(nums).sum();
        System.out.println("数组之和为：" + sum);
    }

    public void test() {
        ParameterizedType a = null;
        TypeVariable b = null;
        GenericArrayType c = null;
        Class d = null;
        WildcardType e = null;

    }

    public static void print(Type type) {
        logger.info("(");
        logger.info("type.toString = {}", type);
        logger.info("type.toJSON = {}", JSONObject.toJSONString(type));
        if (type == null) {
            return;
        }
        logger.info("typeName = {}", type.getTypeName());
        logger.info("typeClass = {}", type.getClass().getName());
        logger.info(")");
    }

    public static void print(ParameterizedType type) {
        logger.info("-----------------ParameterizedType-----------------");
        if (type == null) {
            return;
        }
        logger.info("RawType:获取声明泛型的类或者接口，也就是泛型中<>前面的那个值；");
        Type rawType = type.getRawType();
        print(rawType);
        logger.info("ActualTypeArguments:获取泛型中的实际类型，可能会存在多个泛型，例如Map<K,V>,所以会返回Type[]数组；");
        Type[] actualTypeArguments = type.getActualTypeArguments();
        for (Type actualTypeArgument : actualTypeArguments) {
            print(actualTypeArgument);
        }
        logger.info(
                "OwnerType:获取泛型的拥有者，“拥有者”表示的含义--内部类的“父类”，通过getOwnerType()方法可以获取到内部类的“拥有者”；例如： Map 就是 Map.Entry<String,String>的拥有者");
        Type ownerType = type.getOwnerType();
        print(ownerType);
    }

    public static void print(GenericArrayType type) {
        logger.info("-----------------GenericArrayType-----------------");
        if (type == null) {
            return;
        }
        logger.info(
                "genericComponentType:返回泛型数组中元素的Type类型，即List<String>[] 中的 List<String>(ParameterizedTypeImpl)、T[] 中的T(TypeVariableImpl)");
        logger.info("值得注意的是，无论是几维数组，getGenericComponentType()方法都只会脱去最右边的[]，返回剩下的值；");
        Type genericComponentType = type.getGenericComponentType();
        print(genericComponentType);
    }

    /**
     * 泛型数组类型，例如List<String>[] 、T[]等；
     */
    @Test
    public void genericArrayType() throws NoSuchFieldException, SecurityException {
        GenericArrayType genericArrayType = null;
        Type type = null;
        Field fieldStringArray = TypeDemo.class.getDeclaredField("array");
        type = fieldStringArray.getGenericType();
        print(type);
        genericArrayType = (GenericArrayType) type;
        print(genericArrayType);
        ///////////////////////////////////
        Field fieldListArray = TypeDemo.class.getDeclaredField("listArray");
        type = fieldListArray.getGenericType();
        print(type);
        genericArrayType = (GenericArrayType) type;
        print(genericArrayType);
        ///////////////////////////////
        Field fieldSetArray = TypeDemo.class.getDeclaredField("setArray");
        type = fieldSetArray.getGenericType();
        print(type);
        genericArrayType = (GenericArrayType) type;
        print(genericArrayType);

    }

    /**
     * ParameterizedType表示参数化类型，也就是泛型，例如List<T>、Set<T>等；
     */
    @Test
    public void parameterizedType() throws NoSuchFieldException, SecurityException {
        ParameterizedType parameterizedType = null;
        Type type = null;
        Field fieldCollection = TypeDemo.class.getDeclaredField("collection");
        type = fieldCollection.getGenericType();
        print(type);
        parameterizedType = (ParameterizedType) fieldCollection.getGenericType();
        print(parameterizedType);
        ////////////////////
        Field fieldMap = TypeDemo.class.getDeclaredField("map");
        type = fieldMap.getGenericType();
        print(type);
        parameterizedType = (ParameterizedType) fieldMap.getGenericType();
        print(parameterizedType);
        ////////////////////
        Field fieldMapEntry = TypeDemo.class.getDeclaredField("mapEntry");
        type = fieldMapEntry.getGenericType();
        print(type);
        parameterizedType = (ParameterizedType) fieldMapEntry.getGenericType();
        print(parameterizedType);

        Collection<String> ids = new ArrayList<>();
        Class<?> cls = ids.getClass();
        logger.info(cls.toGenericString());
        String canonicalName = cls.getCanonicalName();
        logger.info(canonicalName);
        TypeVariable<?>[] typeVariables = cls.getTypeParameters();
        for (TypeVariable<?> typeVariable : typeVariables) {
            logger.info(typeVariable.getTypeName());
        }

        Type[] types = cls.getGenericInterfaces();
        for (Type t : types) {
            logger.info(t.getTypeName());
            if (t instanceof ParameterizedType) {
                logger.info("ok");
            }
        }

    }

}

class ParameterizedTypeDemo implements ParameterizedType {

    private final Type ownerType;
    private final Type rawType;
    private final Type[] typeArguments;

    ParameterizedTypeDemo(Type ownerType, Type rawType, Type... typeArguments) {
        // Require an owner type if the raw type needs it.
        if (rawType instanceof Class<?> && (ownerType == null) != (((Class<?>) rawType).getEnclosingClass() == null)) {
            throw new IllegalArgumentException();
        }

        this.ownerType = ownerType;
        this.rawType = rawType;
        this.typeArguments = typeArguments.clone();
    }

    public Type[] getActualTypeArguments() {
        return typeArguments.clone();
    }

    public Type getRawType() {
        return rawType;
    }

    public Type getOwnerType() {
        return ownerType;
    }

}
